package ws

import (
	"errors"
	"strings"
	"time"

	"github.com/google/uuid"
	jsoniter "github.com/json-iterator/go"
)

var json = jsoniter.ConfigCompatibleWithStandardLibrary

// Message
// 基础消息
// 在内部，都需要利用该消息作为信息的载体
type Message struct {
	// 消息的唯一ID
	Mid string `json:"mid"`

	// 消息的发送者，或者是来源
	From string `json:"from"`

	// 消息的接受者，或者目的地
	// like: {name}>>>{type}>>>{value}
	// {name} such as - order, gateway, and so on
	// {type} can be topic, accountId, and so on
	// {value} should be the actual address.
	To string `json:"to"`

	// 消息的时间戳，随着消息创建而创建
	Timestamp MessageTime `json:"timestamp" time_format:"2006-01-02 15:04:05"`

	// 消息的最后修改时间，默认等同于Timestamp
	Updated MessageTime `json:"updated" time_format:"2006-01-02 15:04:05"`

	// 消息的元数据，用于防止描述该消息的一些额外信息。
	// 比如token，agent之类的
	// 可以类比Http协议中的header
	Meta map[string]string `json:"meta"`

	// 消息类型，隐含了业务含义
	Type Type `json:"type"`

	// 消息动作，用于协助接受者触发合适的Handler
	Action Action `json:"action"`

	// 消息主体内容
	Body interface{} `json:"body"`

	// 消息body的格式，比如json, xml, object等
	Format Format `json:"format"`
}

// Type
// 消息类型
type Type string

// 几种常用的默认的消息类型
const (
	Order   Type = "order"
	Account Type = "account"
	Ship    Type = "ship"
	Error   Type = "error"
)

// Action
// 消息动作
type Action string

// Format
// 消息body的格式
type Format string

// 几种常用的Format
const (
	// Text 字符串文本
	Text Format = "text"

	// Json 格式的字符串文本
	Json Format = "json"

	// Xml 格式的字符串文本
	Xml Format = "xml"

	// Object go 对象， 一般需要指定特定的对象。
	// 而且对象名称需要能够被接受者识别出来
	Object Format = "object"
)

type MessageTime time.Time

func (mt MessageTime) MarshalJSON() ([]byte, error) {
	theT := time.Time(mt)

	str := theT.Format("2006-01-02 15:04:05")

	return []byte(`"` + str + `"`), nil
}

func (mt *MessageTime) UnmarshalJSON(data []byte) error {
	var parseStr string
	err := json.Unmarshal(data, &parseStr)
	if err != nil {
		return err
	}

	t, err := time.Parse("2006-01-02 15:04:05", parseStr)
	if err != nil {
		return err
	}

	*mt = MessageTime(t)

	return nil
}

func (mt *MessageTime) ToString() string {
	if mt == nil {
		return ""
	}

	theT := time.Time(*mt)

	return theT.Format("2006-01-02 15:04:05")
}

func (m *Message) ToString() (string, error) {
	if m == nil {
		return "", nil
	}

	bytes, err := json.Marshal(m)
	if err != nil {
		return "", errors.New("message json marshal error")
	}

	return string(bytes), nil
}

var Separator string = ">>>"

func (m *Message) ParseToAddr() {
	if m.To == "" {
		return
	}

	if m.Meta == nil {
		m.Meta = make(map[string]string)
	}

	values := strings.Split(m.To, Separator)
	switch len(values) {
	case 0:
		return
	case 1:
		m.Meta["toAddressValue"] = values[0]
		return
	case 2:
		m.Meta["toType"] = values[0]
		m.Meta["toAddressValue"] = values[1]
		return
	default:
		m.Meta["toService"] = values[0]
		m.Meta["toType"] = values[1]
		m.Meta["toAddressValue"] = values[2]
		return
	}
}

func (m *Message) ToService() string {
	if m.Meta == nil {
		return ""
	}

	return m.Meta["toService"]
}

func (m *Message) ToServiceType() string {
	if m.Meta == nil {
		return ""
	}

	return m.Meta["toType"]
}

func (m *Message) ToAddress() string {
	if m.Meta == nil {
		return ""
	}

	return m.Meta["toAddressValue"]
}

func (m *Message) IsToUser() bool {
	return m.ToServiceType() == "user"
}

func (m *Message) Set(key string, value string) {
	if m.Meta == nil {
		m.Meta = make(map[string]string)
	}

	m.Meta[key] = value
}

func (m *Message) SetAccountId(accId string) *Message {
	m.Set("accountId", accId)
	return m
}

func (m *Message) AccountId() string {
	if m.Meta == nil {
		return ""
	}

	accId := m.Meta["accountId"]
	if accId != "" {
		return accId
	}

	if m.IsToUser() {
		accId = m.ToAddress()
		m.SetAccountId(accId)
	}

	return accId
}

func (m *Message) SetTimestamp(t time.Time) {
	m.Timestamp = MessageTime(t)
}

func (m *Message) GetTimestamp() time.Time {
	return time.Time(m.Timestamp)
}

func (m *Message) SetUpdatedTime(t time.Time) {
	m.Updated = MessageTime(t)
}

func (m *Message) GetUpdatedTime() time.Time {
	return time.Time(m.Updated)
}

func New() *Message {
	now := MessageTime(time.Now())
	return &Message{
		Mid:       uuid.New().String(),
		Timestamp: now,
		Updated:   now,
		Meta:      make(map[string]string),
	}
}

func (m *Message) SetFrom(f string) *Message {
	m.From = f
	return m
}

func (m *Message) SetTo(t string) *Message {
	m.To = t
	return m
}

func (m *Message) WithBody(body interface{}) *Message {
	m.Body = body
	return m
}

func (m *Message) SetType(t Type) *Message {
	m.Type = t
	return m
}

func (m *Message) SetAction(a Action) *Message {
	m.Action = a
	return m
}

func (m *Message) SetFormat(f Format) *Message {
	m.Format = f
	return m
}
